json 和 pickle

用于序列化的两个模块

  • json, 用于字符串和python数据类型间进行转换
  • pickle, 用于python特有的类型 和 python的数据类型间进行转换

json模块提供了四个功能: dumps,dump,loads,load

pickle模块提供了四个功能: dumps,dump,loads,load

dumps是将dict转化成str格式,loads是将str转化成dict格式。

dump和load也是类似的功能,只是与文件操作结合起来了。

两个模块用法相同,区别在于pickle只可用于Python,可序列化Python特有数据类型

json

JSON类型 Python类型
{} dict
[] list
“string” str
1234.56 int或float
true/false True/False
null None

json.dumps()

返回一个str,内容就是标准的JSON.类似的dump()方法可以直接把JSON写入一个file-like Object

>>> import json
>>> d = dict(name='Bob',age=20,score=80)
>>> json.dumps(d)
'{"name": "Bob", "age": 20, "score": 80}'

>>> data = {'k1':123,'k2':'hello'}
# json.dumps 将数据通过特殊的形式转换为所有程序语言都认识的字符串
j_str = json.dumps(data)
print(j_str)

# json.dump 将数据通过特殊的形式转换为所有程序语言都认识的字符串,并写入文件
with open('/User/xxx/result.json','w') as fp:
    json.dump(data,fp)

json.loads()

要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化

>>> json_str = '{"name": "Bob", "age": 20, "score": 80}'
>>> json.loads(json_str)
{'name': 'Bob', 'age': 20, 'score': 80}

由于JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str与JSON的字符串之间转换。

json进阶,序列化类

Python的dict对象可以直接序列化为JSON的{},不过,很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化

import json

class Student:
    def __init__(self,name,age,score):
        self.name = name
        self.age = age
        self.score = score

s = Student('Bob',20,80)
print(json.dumps(s))

上述代码直接运行,会报如下错误

TypeError: <__main__.Student object at 0x1021ded30> is not JSON serializable

原因是Student对象不是一个可序列化为JSON的对象

dumps()方法参考

https://docs.python.org/3/library/json.html#json.dumps

前面的代码不能将Student类实例化为JSON,是因为默认情况下,dumps()方法不知道如何将Student实例变为一个JSON的{}对象

可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:

import json

class Student:
    def __init__(self,name,age,score):
        self.name = name
        self.age = age
        self.score = score

def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

s = Student('Bob',20,80)
print(json.dumps(s,default=student2dict))

这样,Student实例首先被student2dict()转为dict,然后被序列化

不过,如果下次遇到其他类的实例,同样无法实例化为JSON,此时可以这样做,把任意class的实例变为dict

print(json.dumps(s,default=lambda obj:obj.__dict__))

因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class。

同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负责把dict转换为Student实例:

def dict2student(d):
    return Student(d['name'], d['age'], d['score'])
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>

打印出的是反序列化的Student实例对象。

读取json串时保持原有顺序

https://docs.python.org/3.6/library/json.html

文件内容

{
    "1":["张三",150,120,100],
    "2":["李四",90,99,95],
    "3":["王五",60,66,68]
}
with open('student.txt', 'r', encoding='utf8') as f:
    students_info = json.load(f, object_pairs_hook=OrderedDict)

students_info的顺序是跟文件中定义的顺序一样.

实例

将字符串序列化成字典

创建一个字符串变量 dict_str

>>> dict_str = '{"k1":"v1","k2":"v2"}'
>>> type(dict_str)
<class 'str'>

将字符串变量 dict_str 序列化成字典格式

>>> import json

>>> dict_json = json.loads(dict_str)
>>> type(dict_json)
<class 'dict'>
>>> dict_json
{'k2': 'v2', 'k1': 'v1'}

将一个列表类型的变量序列化成字符串类型

小结

json模块的dumps()loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。

pickle

pickle.dump()

>>> import pickle
>>> data = {'k1':123,'k2':'hello'}
# pickle.dumps 将数据通过特殊的形式转换为只有python语言认识的字符串
>>> p_str = pickle.dumps(data)
>>> p_str
b'\x80\x03}q\x00(X\x02\x00\x00\x00k1q\x01K{X\x02\x00\x00\x00k2q\x02X\x05\x00\x00\x00helloq\x03u.'

# pickle.dump 将数据通过特殊的形式转换为只有python语言认识的字符串,并写入文件
with open('/User/xxxx/result.pk','w') as fp:
    pickle.dump(data,fp)

pickle.load()

if os.path.exists(os.path.split(__file__)[0] + '/user.db'):
    pk_file = open('user.db','rb')
    students = pickle.load(pk_file)
    pk_file.close()